<?php
/**
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Jan Wessely <info@jawe.net>
 */
 
// must be run within Dokuwiki
if (!defined('DOKU_INC')) die();
 
if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
 
class helper_plugin_inlinerss extends DokuWiki_Plugin {

	var $confpath;		// Where to look for the config file and the xslt files
	var $datapath;		// Where to look for all locally cached files
	var $configfile;	// Configuration file
	var $fileprefix;	// What feed casual names get prefixed with
	var $xsltfile;		// The default xsltfile
	var $maxage;		// Maximum file age is 1 day before an error is thrown
	
	/**
	 * Constructor loads some config settings
	 */
	function helper_plugin_inlinerss() {
		//TODO make configurable
		global $conf;
		$this->confpath = DOKU_PLUGIN . 'inlinerss/';
		$this->datapath = $conf['savedir'] . '/inlinerss/';
		$this->configfile = 'inlineRSS.conf';
		$this->xsltfile = 'inlineRSS.xsl';
		$this->maxage = 60 * 60 * 24;
	}
 
	function getInfo(){
        return array(
            'author' => 'Jan Wessely',
            'email'  => 'info@jawe.net',
            'date'   => '2007-03-03',
            'name'   => 'inlineRSS plugin (helper plugin)',
            'desc'   => 'Allows inline RSS feeds from any source in any format',
            'url'    => 'http://jawe.net/wiki/proj/dokuwiki/plugins/inlinerss',
        );
	}
 
	function getMethods(){
		$result = array();
		$result[] = array(
			'name'   => 'parse',
			'desc'   => 'parse the feed and apply the XSLT',
			'params' => array(
				"name (required)" => 'string',
				"url" => 'string',
				"timeout" => 'integer',
				"tanslator" => 'string'
			),
			'return' => array('xhtml' => 'string'),
		);
		$result[] = array(
			'name'   => 'loadConfig',
			'desc'   => 'load the configuration for the specified feed',
			'params' => array(
				"name (required)" => 'string',
				"url" => 'string',
				"timeout" => 'integer',
				"tanslator" => 'string'
			),
			'return' => array('config' => 'array'),
		);
		$result[] = array(
			'name'   => 'getDepends',
			'desc'   => 'the filenames of the config file, the xml file and - if specified - the xslt file in an array',
			'params' => array(
				"config (required)" => 'array'
			),
			'return' => array('depends' => 'array'),
		);
		return $result;
	}
	
	function loadConfig($name, $url = "", $timeout = 30, $translator = "") {
		if (!empty($url)) {
			$config = array(
				'name' => $name,
				'url' => $url,
				'timeout' => $timeout <= 0 ? 30 : $timeout,
				'translator' => empty($translator) ? $this->xsltfile : $translator
			);
		} else {
			$config = false;
			$parameters = file($this->_getConfigFilename())
					or die("Error reading config file: " . $this->_getConfigFilename());
			foreach ($parameters as $line) {
				$line = trim($line);

				if (ereg("^[:space:]*#",$line)) {  // skip comments.
					continue;
				}

				$casualname = "";
				$feedURL = "";
				$updatetime = 0;
				$alternateXSLT = "";   
				list($casualname, $feedURL, $updatetime, $alternateXSLT) = explode(",", $line . ",,,,");

				if (strcasecmp($name, $casualname) == 0) {
					$config = array(
						'name' => $name,
						'url' => $feedURL,
						'timeout' => $updatetime <= 0 ? 30 : $updatetime,
						'translator' => empty($alternateXSLT) ? $this->xsltfile : $alternateXSLT
					);
				}
			}
		}
		return $config;
	}
	
	function parse($name,$url = "", $timeout = 30, $translator = "") {
		global $ID;

		// If we have no URL, we're going to have to go looking in the config file
		$config = $this->loadConfig($name, $url, $timeout, $translator);

		if ($config) { // This means we've got something to process           
			$filename = $this->_getXmlFilename($config);
			$xml = $this->_loadCachedFile($config);

			if (empty($xml)) die ("XML feed $name is empty.\n");

			switch (strtoupper($config['translator'])) {

			case "SKIPXSLT":  // Skip the XSLT processing, just bring in the file
				$xslt_result = $xml;
			break;
	  
			case "EVAL":  // Skip XSLT processing and execute the file.  Very dangerous!
				$xslt_result = eval($xml);
			break;
	  
			default:  // Proceed with XSLT processing.
		
				$xsltfilename = $this->_getXsltFilename($config);
				$xsl = file_get_contents($xsltfilename)
						or die("Error reading XSLT file: $xsltfilename");
		
				// This is a switchboard to choose the proper XSLT processing engine and grind through it.
		
				if (PHP_VERSION >= 5) {
					$xslt = new xsltProcessor;
					$xslt->importStyleSheet(DomDocument::loadXML($xsl));
					$xslt_result = $xslt->transformToXML(DomDocument::loadXML($xml));   
		   
				} elseif (function_exists('domxml_open_mem') && function_exists('domxml_xslt_stylesheet')) {  // PHP 4 DOM_XML support
					if (!$domXml = domxml_open_mem($xml)) {
						$result = "Error while parsing the xml document\n";
					}

					$domXsltObj = domxml_xslt_stylesheet( $xsl );
					$domTranObj = $domXsltObj->process( $domXml );
					$xslt_result = $domXsltObj->result_dump_mem( $domTranObj );

				} elseif (function_exists('xslt_create')) {  // PHP 4 XSLT library
					$arguments = array (
						'/_xml' => $xml,
						'/_xsl' => $xsl
					);

					$xslt_inst = xslt_create();    
					$xslt_result = xslt_process($xslt_inst,'arg:/_xml','arg:/_xsl', NULL, $arguments);
					xslt_free($xslt_inst);
		  
				} else {  // Nothing, no valid processor found.  Curses.
					die("No valid XSLT processor found");     
				}
		
				if (empty($xslt_result)) {
					$xslt_result = "XSLT error - verify $filename is a valid XML file and check logs.\n";
				}
			} // End of Switch

			$result = "<!-- inlineRSS - beginning of $name feed -->\n" . $xslt_result . "\n<!-- end of $name feed -->";

		} else {
				$result = "Feed $name not found\n";
		}

		return $result;  
	}
	
	function getDepends($config) {
		$depends = array();
		$depends[] = __FILE__;
		$depends[] = $this->_getConfigFilename();
		$depends[] = $this->_getXmlFilename($config);
		$xsltfilename = $this->_getXsltFilename($config);
		if (!empty($xsltfilename)) {
			$depends[] = $xsltfilename;
		}
		return $depends;
	}

	function _getXmlFilename($config) {
		return $this->datapath . $config['name'] . ".xml";
	}
	
	function _getXsltFilename($config) {
		switch (strtoupper($config['translator'])) {
			case 'SKIPXSLT':
			case 'EVAL':
				return null;
			default:
				return $this->confpath . $config['translator'];
		}
	}
	
	function _getConfigFilename() {
		return $this->confpath . $this->configfile;
	}
	
	function _getCachedFileAge($filename) {
		return time() - (@file_exists($filename) ? filectime($filename) : 0);
	}
	
	function _loadCachedFile($config) {
		$filename = $this->_getXmlFilename($config);
		$xml = false;
		$age = time();

		if ( file_exists($filename)) {
			// We have a local copy, get it just in case
			$xml = file_get_contents($filename);
			$age -= filectime($filename);
		}

		if ( !$xml || $age > $config['timeout'] * 60 ) {  // If there's no file, or it's old
		  
			// revised to now use CURL because, well, it's a tiny bit safer.
	  
			if (function_exists('curl_init') and stristr($config['url'],"http:")) {
				$curl_handle=curl_init();
				curl_setopt($curl_handle, CURLOPT_URL, $config['url']);
				curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 10);
				curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1);
				$curlxml = curl_exec($curl_handle);
				curl_close($curl_handle);

				if (empty($curlxml))  {
					if ($age > $this->maxage or !$xml) {
						die("Error reading feed $name using curl.");
					}
					$writefile = FALSE;
				} else {
					$xml = $curlxml;
					$writefile = TRUE;
				}
			
			} else {
				//  If CURL is giving you problems, use the line below instead.
				$filegetxml = file_get_contents($config['url']);
				if (empty($filegetxml)) {
					if ($age > $this->maxage or !$xml) {
						die("Error reading feed $name using file_get_contents(" . $config['url'] . ").");
					}
					$writefile = FALSE;
				} else {
					$xml = $filegetxml;
					$writefile = TRUE;
				}
			} 
		  
			if ($writefile) {
				if (!io_saveFile($filename, $xml)) {
					die ("Error writing $filename - possible permissions issue - directory permissions are "
							. substr(sprintf('%o', fileperms(dirname($filename))), -4));
				}
			}
		} // if

		return $xml;
	}
	
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :